"""15S集中安装架测试台项目模块"""
from openpyxl.reader.excel import load_workbook
from openpyxl.styles import PatternFill

"""
模块功能：
    1. 囊括了15s集中安装架测试台的测试定义，规定了每一个显示的channel在屏幕上显示的额外信息
    2. 对已经经过JSON协议器解析后的数据再按照15s集中安装架测试台模块的数据进行解析对比，并以列表的形式返回错误的信息
    3. 在此模块中，将15s集中安装架测试台的协议解析函数封装成了一个类，方便调用
"""

from screen.LogControl.log_init import *
from screen.ClassRefactoring.object_refactoring import HoverInfoManager
from openpyxl.styles import PatternFill
from openpyxl.styles.colors import Color
from colorsys import hsv_to_rgb

"""15集中安装架测试台项目模块的协议类"""


class Protocol_15s:
    def __init__(self, main_window):
        self.main_window = main_window

        # 日志配置
        self.log = Log_Config()
        # 默认值
        self.__Default_value = '1' * 123

    """11BS数据解包函数"""

    def protocol_15s_unpack(self, recv_data: bytes):
        """
        15集中安装架测试台协议解析函数
        :param recv_data:待解析的函数
        :return:状态码或者解析后的数据(数据只会将错误的数据返回)
        """
        # 判断功能ID
        if recv_data[0] != 0x04:
            self.log.logger.error("功能ID错误")
            return -1

        # 获取有效数据长度
        length = recv_data[1]

        # 获取有效数据
        data = recv_data[2:length + 2]

        #将数据转换为二进制数据
        #  {0:08b} 是一个格式化字符串，其中0是占位符，表示我们要插入的值（这里是byte）
        # :08b是格式规范，表示输出一个二进制表示，总宽度至少为8位，不够前面补0。
        binary_str_parts = ['{0:08b}'.format(byte) for byte in data]

        #将数据转换为字符串
        binary_str = ''.join(binary_str_parts)

        return self.__protocol_15s_compare(binary_str)

    """数据对比函数"""

    def __protocol_15s_compare(self, recv_data: str):
        """
        15s集中安装架测试台协议数据对比函数， 比较两个长度相同的二进制数据，记录不匹配的位索引。
        :param recv_data:待比较的未知数据，二进制形式，长度为123位。
        :return:不匹配位的索引列表。
        """
        try:
            # 判断长度
            assert len(recv_data) - 5 == len(self.__Default_value), "15s模块对比的数据长度不一致"
            assert len(recv_data) >= 123, "15s模块对比的数据长度不足123位"

            # 按位对比并返回不同的索引值
            # zip函数是将两个字符串配对，形成一个迭代器。迭代器中的每一个元素都是一对字符，分别对应两个字符串中的相同位置的字符，比如(r1,u1),(r2,u2),(r3,u3)
            # enumerate函数将迭代器中的每一个元素转化为一个元组，元组的第一个元素是索引，第二个元素是迭代器中的元素。比如(0,(r1,u1)),(1,(r2,u2)),(2,(r3,u3))
            mismatch_indices = [i + 1 for i, (rd, ud) in enumerate(zip(self.__Default_value, recv_data)) if rd != ud]

            return mismatch_indices

        except AssertionError as e:
            self.log.logger.error(e)
            return None

    def _15s_initialize_hover_info(self):
        """
        在UI初始化完成后设置悬停信息
        """
        self.hoverManager = HoverInfoManager()

        # 循环绑定通道中文信息
        for i in range(1, 124):
            index = min((i - 1) // 128 + 1, 3)  # i从1开始，因此减1再除以128，加1得到index，同时限制最大为3
            second_index = 128 if i in (128, 256) else i % 128
            channel_name = f'channel_{index}_{second_index}'
            channel_widget = getattr(self.main_window, channel_name, None)
            if channel_widget:
                self.hoverManager.add_hover_info(channel_widget, self.__get_channel_name(i))

    """获取通道中文名称"""

    def __get_channel_name(self, channel_number: int) -> str:
        """
        获取通道中文名称
        :param channel_number:通道编号
        :return:通道中文名称
        """
        # 123个通道元素：参考15s模块的通道测试定义
        self.Chinese = {1: "COM2出＋", 2: "COM2出－", 3: "COM2出+", 4: "COM2出-", 5: "COM1 出+",
                        6: "COM1  出－", 7: "JIDSl出＋",
                        8: "JIDSl出－", 9: "J1DS2 出＋", 10: "J1DS2 出－", 11: "地勤话筒入+", 12: "地勤话筒入-",
                        13: "地勤话筒出+", 14: "地勤话筒出-",
                        15: "COM2 入＋", 16: "COM2A－", 17: "COMlA＋", 18: "COMl入－", 19: "JIDSI入＋",
                        20: "JIDSl入－", 21: "JIDS2入＋",
                        22: "JIDS2A－", 23: "TACAN入＋", 24: "TACANA－", 25: "MLS入＋", 26: "MLSA－",
                        27: "WL 入＋", 28: "WL 入-",
                        29: "XB 入＋", 30: "XB 入-", 31: "SMS 入十", 32: "SMS 地", 33: "到显控盒耳机出＋",
                        34: "到显控盒耳机出-", 35: "VTR 出+",
                        36: "VTR 出－", 37: "UHF-IN＋", 38: "UHF-IN－", 39: "UHF-OUT+", 40: "UHF-OUT-",
                        41: "JIDS1PTTA", 42: "JIDS2PTT入",
                        43: "超短波话台 PTT", 44: "COM2PTT 入", 45: "COM1PTT 出", 46: "信标机自检", 47: "灯测试信号",
                        48: "CCU 故障指示",
                        49: "过台信号", 50: "UHF-PTT", 51: "电源指示", 52: "液压故障", 53: "解尾旋模式",
                        54: "右发超温", 55: "一个液压故障",
                        56: "两台发电机故障", 57: "发动机在训练状态", 58: "俯仰角速率传感器3次故障", 59: "模拟故障",
                        60: "模拟备份工作", 61: "供电管理转手动",
                        62: "无线电高度静默信号",
                        63: "启动霄达应答机自检＋", 64: "动霄达应答机自检-", 65: "雷达应答机自检结果十",
                        66: "霄达应答机自检结果-", 67: "BPU 到显示控制盒＋12v", 68: "BPU 到显示控制盒+12V 地",
                        69: "BPU 到显示控制盒串 口发＋",
                        70: "BPU 到显示控制盒串 口发－", 71: "毗到显示控制盒串☐收+", 72: "刚到显示控制盒市☐收-",
                        73: "AUX422CO+",
                        74: "AUX422CO-", 75: "AUX422CI+", 76: "AUX422CI-",
                        77: "AX22CRTS+", 78: "AX22CRTS-", 79: "AX22CCTS+", 80: "AX22CCTS-",
                        81: "BPU-CCU", 82: "PGND", 83: "CCU27V电源",
                        84: "CCU 27V  电源", 85: "BPU 27V 电源", 86: "BPU 27V 电源", 87: "机载 27V 地",
                        88: "机载 27V 地", 89: "COM3 27V电源", 90: "机载 27V地",
                        91: "注钥下传", 92: "注钥上传", 93: "COM3发+", 94: "COM3发-", 95: "+5V",
                        96: "+12V", 97: "到后舱显示控制盒耳机出+",
                        98: "到后舱显示控制盒耳机出一", 99: "到后舱显示控制盒耳机出+", 100: "到后舱显示控制盒耳机出-",
                        101: "后舱JIDS1PTT入", 102: "后舱JIDS2PTT入", 103: "前舱显控盒内话PTT",
                        104: "后舱显控盒内话PTT", 105: "前舱显控盒'同发'PTT", 106: "后舱显控盒'同发'PTT",
                        107: "短波辅助PTT", 108: "短波音频出+",
                        109: "短波音频出-", 110: "短波音频入+",
                        111: "短波音频入-", 112: "任务机-905+", 113: "任务机-905-", 114: "905-任务机+",
                        115: "905-任务机-", 116: "906-飞控RS422+",
                        117: "905-飞控RS422-", 118: "后舱COM1/3PTT入", 119: "后舱COM2PTT入",
                        120: "BPU到后船显不控制盒串☐发+", 121: "BPU到后舱显示控制盒串☐发-",
                        122: "BPU到后舱显示控制盒串☐收+",
                        123: "BPU到后船显示控制盒串☐收-"}

        # 遍历字典的键并返回值
        if channel_number in self.Chinese:
            return self.Chinese[channel_number]
        else:
            self.log.logger.error("15s模块通道编号错误")
            return " "

    def __generate_color_map(self, n):
        """
        生成n个不同的颜色，用于标记不同的错误通道号。
        :param n: 需要的颜色数量
        :return: 包含PatternFill对象的颜色映射字典
        """
        colors = {}
        step = 1.0 / n
        for i in range(n):
            hue = i * step
            rgb = hsv_to_rgb(hue, 1.0, 1.0)
            color = Color(rgb=f"{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}")
            fill = PatternFill(start_color=color.rgb, end_color=color.rgb, fill_type='solid')
            colors[i] = fill
        return colors

    def _15s_erro_info_save_excel(self, info: dict):
        """
        将错误列表的信息定位到文档中，并生成一个新的错误文档。
        :param info:错误列表
        :return:None
        """
        try:
            # 文件路径
            file_path = 'Doc/StandardForm/15S测试定义查询表格.xlsx'
            # 输出文件路径
            output_path = 'Doc/ErroForm/15S测试结果错误表格.xlsx'

            # 加载Excel工作簿
            wb = load_workbook(file_path)
            sheet = wb.active

            # 匹配列名
            column_to_match = "序号"

            # 错误字典
            values_to_style = info

            # 获取颜色字典
            color_map = self.__generate_color_map(len(values_to_style))

            # 默认填充样式
            default_fill = PatternFill(fill_type=None)

            # 获取“序号”列的索引
            header_row = next(sheet.iter_rows(min_row=2, max_row=3, values_only=True))
            try:
                column_index = header_row.index(column_to_match) + 1  # 索引从1开始
            except ValueError:
                self.log.logger.error(f"列名'{column_to_match}'未找到，请检查列名是否正确。")
                return None

            # 创建一个字典，用于存储“序号”到行号的映射
            number_to_row = {row[column_index - 1]: idx for idx, row in
                             enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2)}

            # 清除所有单元格的现有填充样式，以避免样式叠加
            for row in sheet.iter_rows(min_row=2, max_col=sheet.max_column):
                for cell in row:
                    cell.fill = default_fill

            # 遍历错误字典中的每个键值对
            for i, (main_channel, connected_channels) in enumerate(values_to_style.items()):
                # 确定color_map中的键
                map_key = i % len(color_map)
                # 应用样式到主通道的行
                if main_channel in number_to_row:
                    row_num = number_to_row[main_channel]
                    # 更改B和C列的单元格样式
                    for col in sheet.iter_cols(min_col=2, max_col=3, min_row=row_num, max_row=row_num):
                        for cell in col:
                            cell.fill = color_map[map_key]

                # 应用样式到与主通道错误连接的其他通道的行
                for connected_channel in connected_channels:
                    if connected_channel in number_to_row:
                        row_num = number_to_row[connected_channel]
                        # 更改D和E列的单元格样式
                        for col in sheet.iter_cols(min_col=4, max_col=5, min_row=row_num, max_row=row_num):
                            for cell in col:
                                cell.fill = color_map[map_key]

            # 保存更改到原文件
            wb.save(output_path)
            wb.close()
            return True

        except Exception as e:
            self.log.logger.error(f"错误信息生成文档失败:{e}")
            return False
